import cv2
import mediapipe as mp
import numpy as np
import time
from PIL import Image, ImageDraw, ImageFont

# 初始化手部检测模型
mp_hands = mp.solutions.hands
hands = mp_hands.Hands(
    max_num_hands=1,
    min_detection_confidence=0.6,
    min_tracking_confidence=0.6,
    model_complexity=1  # 增加模型复杂度以提高精度
)

# 手势映射表（修正键值对应）
GESTURE_MAP = {0: "石头", 1: "剪刀", 2: "布", -1: "未识别", -2: "手背", 3: "中指"}
# 必胜策略（修正中指的映射）
WINNING_STRATEGY = {0: 2, 1: 0, 2: 1, 3: 0}  # 中指对手默认出石头


# 解决OpenCV中文显示问题的函数
def cv2_put_chinese_text(img, text, position, font_size=24, color=(0, 255, 0)):
    """在OpenCV图像上绘制中文文字"""
    try:
        rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
        pil_img = Image.fromarray(rgb_img)
        draw = ImageDraw.Draw(pil_img)
        try:
            font = ImageFont.truetype("simhei.ttf", font_size)
        except IOError:
            try:
                font = ImageFont.load_default()
                print("警告：未找到中文字体，使用默认字体")
            except Exception as e:
                print(f"错误：无法加载任何字体 - {e}")
                return img
        draw.text(position, text, font=font, fill=color)
        return cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
    except Exception as e:
        print(f"绘制文字时出错: {e}")
        return img


# 加载手势图片
def load_gesture_images():
    """加载并返回三种手势的图片"""
    try:
        rock_img = cv2.imread("rock.png")
        scissors_img = cv2.imread("scissors.png")
        paper_img = cv2.imread("paper.png")
        fuck_img = cv2.imread("OIP-C.png")

        if rock_img is not None:
            rock_img = cv2.resize(rock_img, (200, 200))
        if scissors_img is not None:
            scissors_img = cv2.resize(scissors_img, (200, 200))
        if paper_img is not None:
            paper_img = cv2.resize(paper_img, (200, 200))
        if fuck_img is not None:
            fuck_img = cv2.resize(fuck_img, (200, 200))

        return {0: rock_img, 1: scissors_img, 2: paper_img, 3: fuck_img}
    except Exception as e:
        print(f"加载手势图片时出错: {e}")
        return None


# 改进的手势识别函数（修正剪刀手势判断）
def recognize_gesture(hand_landmarks, hand_info):
    """根据手部关键点和左右手信息识别手势"""
    landmarks = hand_landmarks.landmark

    # 判断手指是否伸直
    thumb_open = landmarks[mp_hands.HandLandmark.THUMB_TIP].x > landmarks[mp_hands.HandLandmark.THUMB_IP].x
    index_open = landmarks[mp_hands.HandLandmark.INDEX_FINGER_TIP].y < landmarks[
        mp_hands.HandLandmark.INDEX_FINGER_PIP].y
    middle_open = landmarks[mp_hands.HandLandmark.MIDDLE_FINGER_TIP].y < landmarks[
        mp_hands.HandLandmark.MIDDLE_FINGER_PIP].y
    ring_open = landmarks[mp_hands.HandLandmark.RING_FINGER_TIP].y < landmarks[mp_hands.HandLandmark.RING_FINGER_PIP].y
    pinky_open = landmarks[mp_hands.HandLandmark.PINKY_TIP].y < landmarks[mp_hands.HandLandmark.PINKY_PIP].y

    # 统计伸直的手指数量
    fingers_open = sum([thumb_open, index_open, middle_open, ring_open, pinky_open])

    # 检查是否为手背
    is_back_of_hand = landmarks[mp_hands.HandLandmark.WRIST].z < -0.05

    # 左右手信息
    is_right_hand = hand_info.classification[0].label == 'Right'

    # 根据伸直的手指数量判断手势
    if fingers_open == 0:
        return 0  # 石头（全握拳）
    elif fingers_open == 1 and middle_open:
        return 3  # 中指（仅中指伸直）
    elif fingers_open == 2 and index_open and middle_open and not ring_open and not pinky_open:
        return 1  # 剪刀（只有食指和中指伸直）
    elif fingers_open == 5:
        return 2  # 布（五指张开）
    elif is_back_of_hand:
        return -2  # 返回-2表示检测到的是手背
    else:
        return -1  # 未识别的手势


# 在画面上显示结果（增加对中指手势的处理）
def display_result(frame, player_gesture, bot_gesture, gesture_images):
    """在画面上显示玩家和机器的手势结果"""
    height, width, _ = frame.shape

    # 显示玩家手势
    if player_gesture == -2:
        frame = cv2_put_chinese_text(frame, "检测到手背，请展示手掌", (10, 30), 24, (0, 0, 255))
    else:
        frame = cv2_put_chinese_text(frame, f"你: {GESTURE_MAP[player_gesture]}", (10, 30), 24, (0, 255, 0))

    # 显示机器手势（处理所有有效手势）
    if player_gesture in [0, 1, 2, 3]:
        if gesture_images and gesture_images.get(bot_gesture) is not None:
            bot_img = gesture_images[bot_gesture]
            img_height, img_width, _ = bot_img.shape
            frame[10:10 + img_height, width - img_width - 10:width - 10] = bot_img
        else:
            frame = cv2_put_chinese_text(frame, f"机器: {GESTURE_MAP[bot_gesture]}",
                                         (width - 200, 30), 24, (0, 0, 255))

        # 显示结果
        result_text = "机器赢!" if player_gesture != 3 else "请注意文明游戏"
        frame = cv2_put_chinese_text(frame, result_text, (width // 2 - 100, height - 20), 30,
                                     (255, 0, 0) if player_gesture != 3 else (0, 0, 255))
    else:
        # 玩家手势无效时显示提示
        frame = cv2_put_chinese_text(frame, "请做出石头、剪刀或布的手势", (width // 2 - 150, height - 20), 24,
                                     (255, 165, 0))

    return frame


def main():
    # 加载手势图片
    gesture_images = load_gesture_images()

    # 打开摄像头
    cap = cv2.VideoCapture(0)

    # 设置摄像头分辨率
    cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
    cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480)

    # 初始化变量
    current_player_gesture = -1  # 当前玩家手势
    current_bot_gesture = -1  # 当前机器手势
    last_gesture_time = 0  # 上次识别时间
    gesture_stability_threshold = 0.5  # 手势稳定需要的时间（秒）
    last_detected_gesture = -1  # 上次检测到的手势

    cv2.namedWindow("猜拳游戏 - 按ESC退出", cv2.WINDOW_NORMAL)

    while cap.isOpened():
        success, frame = cap.read()
        if not success:
            break

        # 镜像翻转画面
        frame = cv2.flip(frame, 1)

        # 转换为RGB格式
        rgb_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # 检测手部
        results = hands.process(rgb_frame)

        # 当前检测到的手势
        detected_gesture = -1

        # 检查是否检测到手部和手部信息
        if results.multi_hand_landmarks and results.multi_handedness:
            for hand_landmarks, hand_info in zip(results.multi_hand_landmarks, results.multi_handedness):
                # 绘制手部关键点和连接线
                mp.solutions.drawing_utils.draw_landmarks(
                    frame, hand_landmarks, mp_hands.HAND_CONNECTIONS)

                # 识别手势，传入手部信息
                detected_gesture = recognize_gesture(hand_landmarks, hand_info)

                # 如果检测到有效手势（包括中指）
                if detected_gesture in [0, 1, 2, 3]:
                    current_time = time.time()

                    # 如果手势与上次相同，且持续时间超过阈值
                    if detected_gesture == last_detected_gesture and \
                            current_time - last_gesture_time > gesture_stability_threshold:
                        # 只有当用户做出新的不同手势时，机器才会改变回应
                        if detected_gesture != current_player_gesture:
                            current_player_gesture = detected_gesture
                            current_bot_gesture = WINNING_STRATEGY[detected_gesture]
                            print(
                                f"用户出: {GESTURE_MAP[detected_gesture]}, 机器出: {GESTURE_MAP[current_bot_gesture]}")
                    else:
                        # 记录第一次检测到该手势的时间
                        if detected_gesture != last_detected_gesture:
                            last_gesture_time = current_time

                    # 更新最后检测到的手势
                    last_detected_gesture = detected_gesture

        # 显示结果
        frame = display_result(frame, current_player_gesture, current_bot_gesture, gesture_images)

        # 显示画面
        cv2.imshow("猜拳游戏 - 按ESC退出", frame)

        # 按ESC键退出
        if cv2.waitKey(1) == 27:
            break

    # 释放资源
    cap.release()
    cv2.destroyAllWindows()


if __name__ == "__main__":
    main()